home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 4: GNU Archives / Linux Cubed Series 4 - GNU Archives.iso / gnu / gawk-3.000 / gawk-3 / gawk-3.0.0 / node.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-12-19  |  9.4 KB  |  490 lines

  1. /*
  2.  * node.c -- routines for node management
  3.  */
  4.  
  5. /* 
  6.  * Copyright (C) 1986, 1988, 1989, 1991-1995 the Free Software Foundation, Inc.
  7.  * 
  8.  * This file is part of GAWK, the GNU implementation of the
  9.  * AWK Programming Language.
  10.  * 
  11.  * GAWK is free software; you can redistribute it and/or modify
  12.  * it under the terms of the GNU General Public License as published by
  13.  * the Free Software Foundation; either version 2 of the License, or
  14.  * (at your option) any later version.
  15.  * 
  16.  * GAWK is distributed in the hope that it will be useful,
  17.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  18.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19.  * GNU General Public License for more details.
  20.  * 
  21.  * You should have received a copy of the GNU General Public License
  22.  * along with this program; if not, write to the Free Software
  23.  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
  24.  */
  25.  
  26. #include "awk.h"
  27.  
  28. /* r_force_number --- force a value to be numeric */
  29.  
  30. AWKNUM
  31. r_force_number(n)
  32. register NODE *n;
  33. {
  34.     register char *cp;
  35.     register char *cpend;
  36.     char save;
  37.     char *ptr;
  38.     unsigned int newflags;
  39.  
  40. #ifdef DEBUG
  41.     if (n == NULL)
  42.         cant_happen();
  43.     if (n->type != Node_val)
  44.         cant_happen();
  45.     if(n->flags == 0)
  46.         cant_happen();
  47.     if (n->flags & NUM)
  48.         return n->numbr;
  49. #endif
  50.  
  51.     /* all the conditionals are an attempt to avoid the expensive strtod */
  52.  
  53.     n->numbr = 0.0;
  54.     n->flags |= NUM;
  55.  
  56.     if (n->stlen == 0)
  57.         return 0.0;
  58.  
  59.     cp = n->stptr;
  60.     if (isalpha(*cp))
  61.         return 0.0;
  62.  
  63.     cpend = cp + n->stlen;
  64.     while (cp < cpend && isspace(*cp))
  65.         cp++;
  66.     if (cp == cpend || isalpha(*cp))
  67.         return 0.0;
  68.  
  69.     if (n->flags & MAYBE_NUM) {
  70.         newflags = NUMBER;
  71.         n->flags &= ~MAYBE_NUM;
  72.     } else
  73.         newflags = 0;
  74.     if (cpend - cp == 1) {
  75.         if (isdigit(*cp)) {
  76.             n->numbr = (AWKNUM)(*cp - '0');
  77.             n->flags |= newflags;
  78.         }
  79.         return n->numbr;
  80.     }
  81.  
  82.     errno = 0;
  83.     save = *cpend;
  84.     *cpend = '\0';
  85.     n->numbr = (AWKNUM) strtod((const char *) cp, &ptr);
  86.  
  87.     /* POSIX says trailing space is OK for NUMBER */
  88.     while (isspace(*ptr))
  89.         ptr++;
  90.     *cpend = save;
  91.     /* the >= should be ==, but for SunOS 3.5 strtod() */
  92.     if (errno == 0 && ptr >= cpend)
  93.         n->flags |= newflags;
  94.     else
  95.         errno = 0;
  96.  
  97.     return n->numbr;
  98. }
  99.  
  100. /*
  101.  * the following lookup table is used as an optimization in force_string
  102.  * (more complicated) variations on this theme didn't seem to pay off, but 
  103.  * systematic testing might be in order at some point
  104.  */
  105. static const char *values[] = {
  106.     "0",
  107.     "1",
  108.     "2",
  109.     "3",
  110.     "4",
  111.     "5",
  112.     "6",
  113.     "7",
  114.     "8",
  115.     "9",
  116. };
  117. #define    NVAL    (sizeof(values)/sizeof(values[0]))
  118.  
  119. /* r_force_string --- force a value to be a string */
  120.  
  121. NODE *
  122. r_force_string(s)
  123. register NODE *s;
  124. {
  125.     char buf[128];
  126.     register char *sp = buf;
  127.     double val;
  128.  
  129. #ifdef DEBUG
  130.     if (s == NULL)
  131.         cant_happen();
  132.     if (s->type != Node_val)
  133.         cant_happen();
  134.     if ((s->flags & STR) != 0
  135.         && (s->stfmt == -1 || s->stfmt == CONVFMTidx))
  136.         return s;
  137.     if ((s->flags & NUM) == 0)
  138.         cant_happen();
  139.     if (s->stref <= 0)
  140.         cant_happen();
  141. #endif
  142.  
  143.     /* not an integral value, or out of range */
  144.     if ((val = double_to_int(s->numbr)) != s->numbr
  145.         || val < LONG_MIN || val > LONG_MAX) {
  146. #ifdef GFMT_WORKAROUND
  147.         NODE *dummy, *r;
  148.         unsigned short oflags;
  149.         extern NODE *format_tree P((const char *, int, NODE *));
  150.         extern NODE **fmt_list;          /* declared in eval.c */
  151.  
  152.         /* create dummy node for a sole use of format_tree */
  153.         getnode(dummy);
  154.         dummy->lnode = s;
  155.         dummy->rnode = NULL;
  156.         oflags = s->flags;
  157.         s->flags |= PERM; /* prevent from freeing by format_tree() */
  158.         r = format_tree(CONVFMT, fmt_list[CONVFMTidx]->stlen, dummy);
  159.         s->flags = oflags;
  160.         s->stfmt = (char) CONVFMTidx;
  161.         s->stlen = r->stlen;
  162.         s->stptr = r->stptr;
  163.         freenode(r);        /* Do not free_temp(r)!  We want */
  164.         freenode(dummy);    /* to keep s->stptr == r->stpr.  */
  165.  
  166.         goto no_malloc;
  167. #else
  168.         /*
  169.          * no need for a "replacement" formatting by gawk,
  170.          * just use sprintf
  171.          */
  172.         sprintf(sp, CONVFMT, s->numbr);
  173.         s->stlen = strlen(sp);
  174.         s->stfmt = (char) CONVFMTidx;
  175. #endif /* GFMT_WORKAROUND */
  176.     } else {
  177.         /* integral value */
  178.             /* force conversion to long only once */
  179.         register long num = (long) val;
  180.         if (num < NVAL && num >= 0) {
  181.             sp = (char *) values[num];
  182.             s->stlen = 1;
  183.         } else {
  184.             (void) sprintf(sp, "%ld", num);
  185.             s->stlen = strlen(sp);
  186.         }
  187.         s->stfmt = -1;
  188.     }
  189.     emalloc(s->stptr, char *, s->stlen + 2, "force_string");
  190.     memcpy(s->stptr, sp, s->stlen+1);
  191. #ifdef GFMT_WORKAROUND
  192. no_malloc:
  193. #endif /* GFMT_WORKAROUND */
  194.     s->stref = 1;
  195.     s->flags |= STR;
  196.     return s;
  197. }
  198.  
  199. /*
  200.  * dupnode:
  201.  * Duplicate a node.  (For strings, "duplicate" means crank up the
  202.  * reference count.)
  203.  */
  204.  
  205. NODE *
  206. dupnode(n)
  207. NODE *n;
  208. {
  209.     register NODE *r;
  210.  
  211.     if ((n->flags & TEMP) != 0) {
  212.         n->flags &= ~TEMP;
  213.         n->flags |= MALLOC;
  214.         return n;
  215.     }
  216.     if ((n->flags & (MALLOC|STR)) == (MALLOC|STR)) {
  217.         if (n->stref < LONG_MAX)
  218.             n->stref++;
  219.         return n;
  220.     }
  221.     getnode(r);
  222.     *r = *n;
  223.     r->flags &= ~(PERM|TEMP);
  224.     r->flags |= MALLOC;
  225.     if (n->type == Node_val && (n->flags & STR) != 0) {
  226.         r->stref = 1;
  227.         emalloc(r->stptr, char *, r->stlen + 2, "dupnode");
  228.         memcpy(r->stptr, n->stptr, r->stlen);
  229.         r->stptr[r->stlen] = '\0';
  230.     }
  231.     return r;
  232. }
  233.  
  234. /* mk_number --- allocate a node with defined number */
  235.  
  236. NODE *
  237. mk_number(x, flags)
  238. AWKNUM x;
  239. unsigned int flags;
  240. {
  241.     register NODE *r;
  242.  
  243.     getnode(r);
  244.     r->type = Node_val;
  245.     r->numbr = x;
  246.     r->flags = flags | SCALAR;
  247. #ifdef DEBUG
  248.     r->stref = 1;
  249.     r->stptr = 0;
  250.     r->stlen = 0;
  251. #endif
  252.     return r;
  253. }
  254.  
  255. /* make_str_node --- make a string node */
  256.  
  257. NODE *
  258. make_str_node(s, len, flags)
  259. char *s;
  260. size_t len;
  261. int flags;
  262. {
  263.     register NODE *r;
  264.  
  265.     getnode(r);
  266.     r->type = Node_val;
  267.     r->flags = (STRING|STR|MALLOC|SCALAR);
  268.     if (flags & ALREADY_MALLOCED)
  269.         r->stptr = s;
  270.     else {
  271.         emalloc(r->stptr, char *, len + 2, s);
  272.         memcpy(r->stptr, s, len);
  273.     }
  274.     r->stptr[len] = '\0';
  275.            
  276.     if ((flags & SCAN) != 0) {    /* scan for escape sequences */
  277.         char *pf;
  278.         register char *ptm;
  279.         register int c;
  280.         register char *end;
  281.  
  282.         end = &(r->stptr[len]);
  283.         for (pf = ptm = r->stptr; pf < end;) {
  284.             c = *pf++;
  285.             if (c == '\\') {
  286.                 c = parse_escape(&pf);
  287.                 if (c < 0) {
  288.                     if (do_lint)
  289.                         warning("backslash at end of string");
  290.                     c = '\\';
  291.                 }
  292.                 *ptm++ = c;
  293.             } else
  294.                 *ptm++ = c;
  295.         }
  296.         len = ptm - r->stptr;
  297.         erealloc(r->stptr, char *, len + 1, "make_str_node");
  298.         r->stptr[len] = '\0';
  299.         r->flags |= PERM;
  300.     }
  301.     r->stlen = len;
  302.     r->stref = 1;
  303.     r->stfmt = -1;
  304.  
  305.     return r;
  306. }
  307.  
  308. /* tmp_string --- allocate a temporary string */
  309.  
  310. NODE *
  311. tmp_string(s, len)
  312. char *s;
  313. size_t len;
  314. {
  315.     register NODE *r;
  316.  
  317.     r = make_string(s, len);
  318.     r->flags |= TEMP;
  319.     return r;
  320. }
  321.  
  322. /* more_nodes --- allocate more nodes */
  323.  
  324. #define NODECHUNK    100
  325.  
  326. NODE *nextfree = NULL;
  327.  
  328. NODE *
  329. more_nodes()
  330. {
  331.     register NODE *np;
  332.  
  333.     /* get more nodes and initialize list */
  334.     emalloc(nextfree, NODE *, NODECHUNK * sizeof(NODE), "newnode");
  335.     for (np = nextfree; np < &nextfree[NODECHUNK - 1]; np++) {
  336.         np->flags = 0;
  337.         np->nextp = np + 1;
  338.     }
  339.     np->nextp = NULL;
  340.     np = nextfree;
  341.     nextfree = nextfree->nextp;
  342.     return np;
  343. }
  344.  
  345. #ifdef DEBUG
  346. /* freenode --- release a node back to the pool */
  347.  
  348. void
  349. freenode(it)
  350. NODE *it;
  351. {
  352.     it->flags &= ~SCALAR;
  353. #ifdef MPROF
  354.     it->stref = 0;
  355.     free((char *) it);
  356. #else    /* not MPROF */
  357.     /* add it to head of freelist */
  358.     it->nextp = nextfree;
  359.     nextfree = it;
  360. #endif    /* not MPROF */
  361. }
  362. #endif    /* DEBUG */
  363.  
  364. /* unref --- remove reference to a particular node */
  365.  
  366. void
  367. unref(tmp)
  368. register NODE *tmp;
  369. {
  370.     if (tmp == NULL)
  371.         return;
  372.     if ((tmp->flags & PERM) != 0)
  373.         return;
  374.     if ((tmp->flags & (MALLOC|TEMP)) != 0) {
  375.         tmp->flags &= ~TEMP;
  376.         if ((tmp->flags & STR) != 0) {
  377.             if (tmp->stref > 1) {
  378.                 if (tmp->stref != LONG_MAX)
  379.                     tmp->stref--;
  380.                 return;
  381.             }
  382.             free(tmp->stptr);
  383.         }
  384.         freenode(tmp);
  385.     }
  386. }
  387.  
  388. /*
  389.  * parse_escape:
  390.  *
  391.  * Parse a C escape sequence.  STRING_PTR points to a variable containing a
  392.  * pointer to the string to parse.  That pointer is updated past the
  393.  * characters we use.  The value of the escape sequence is returned. 
  394.  *
  395.  * A negative value means the sequence \ newline was seen, which is supposed to
  396.  * be equivalent to nothing at all. 
  397.  *
  398.  * If \ is followed by a null character, we return a negative value and leave
  399.  * the string pointer pointing at the null character. 
  400.  *
  401.  * If \ is followed by 000, we return 0 and leave the string pointer after the
  402.  * zeros.  A value of 0 does not mean end of string.  
  403.  *
  404.  * Posix doesn't allow \x.
  405.  */
  406.  
  407. int
  408. parse_escape(string_ptr)
  409. char **string_ptr;
  410. {
  411.     register int c = *(*string_ptr)++;
  412.     register int i;
  413.     register int count;
  414.  
  415.     switch (c) {
  416.     case 'a':
  417.         return BELL;
  418.     case 'b':
  419.         return '\b';
  420.     case 'f':
  421.         return '\f';
  422.     case 'n':
  423.         return '\n';
  424.     case 'r':
  425.         return '\r';
  426.     case 't':
  427.         return '\t';
  428.     case 'v':
  429.         return '\v';
  430.     case '\n':
  431.         return -2;
  432.     case 0:
  433.         (*string_ptr)--;
  434.         return -1;
  435.     case '0':
  436.     case '1':
  437.     case '2':
  438.     case '3':
  439.     case '4':
  440.     case '5':
  441.     case '6':
  442.     case '7':
  443.         i = c - '0';
  444.         count = 0;
  445.         while (++count < 3) {
  446.             if ((c = *(*string_ptr)++) >= '0' && c <= '7') {
  447.                 i *= 8;
  448.                 i += c - '0';
  449.             } else {
  450.                 (*string_ptr)--;
  451.                 break;
  452.             }
  453.         }
  454.         return i;
  455.     case 'x':
  456.         if (do_lint) {
  457.             static int didwarn = FALSE;
  458.  
  459.             if (! didwarn) {
  460.                 didwarn = TRUE;
  461.                 warning("POSIX does not allow \"\\x\" escapes");
  462.             }
  463.         }
  464.         if (do_posix)
  465.             return ('x');
  466.         if (! isxdigit((*string_ptr)[1])) {
  467.             warning("no hex digits in \\x escape sequence");
  468.             return ('x');
  469.         }
  470.         i = 0;
  471.         for (;;) {
  472.             if (isxdigit((c = *(*string_ptr)++))) {
  473.                 i *= 16;
  474.                 if (isdigit(c))
  475.                     i += c - '0';
  476.                 else if (isupper(c))
  477.                     i += c - 'A' + 10;
  478.                 else
  479.                     i += c - 'a' + 10;
  480.             } else {
  481.                 (*string_ptr)--;
  482.                 break;
  483.             }
  484.         }
  485.         return i;
  486.     default:
  487.         return c;
  488.     }
  489. }
  490.